/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __XMLSAVER_H__
#define __XMLSAVER_H__

#pragma warning(disable:4355)

#include "Object.h"
#include <textstream/TextStream.h>
#include <textstream/Buffer.h>
#include <NoCopy.h>
#include <string>

namespace liba
{
namespace xml
{
    
    
template<class Sym>
struct SaverTraits
{};
    
template<>
struct SaverTraits<char> : public converter::CharTraits<char>
{
    static const converter::Sequence<char> header;
    static const converter::Sequence<char> finish;
    static const converter::Sequence<char> node_start;
    static const converter::Sequence<char> empty_node_finish;
    static const converter::Sequence<char> node_finish;
    static const converter::Sequence<char> attr_value_begin;
    static const converter::Sequence<char> attr_value_apos_begin;
    static const converter::Sequence<char> quote_alias;
};
    
template<>
struct SaverTraits<wchar_t> : public converter::CharTraits<wchar_t>
{
    static const converter::Sequence<wchar_t> header;
    static const converter::Sequence<wchar_t> finish;
    static const converter::Sequence<wchar_t> node_start;
    static const converter::Sequence<wchar_t> empty_node_finish;
    static const converter::Sequence<wchar_t> node_finish;
    static const converter::Sequence<wchar_t> attr_value_begin;
    static const converter::Sequence<wchar_t> attr_value_apos_begin;
    static const converter::Sequence<wchar_t> quote_alias;
};

class SaverBase
{
	template<class Sym>
	bool save_attribute_internal_correct_quote( const std::basic_string<Sym> & name, const Sym * attr_begin, const Sym * attr_end )
	{
		if( !std::char_traits<Sym>::find( attr_begin, attr_end - attr_begin, SaverTraits<Sym>::QUOTE ) )
			return save_attribute_internal( name, attr_begin, attr_end );
		if( !std::char_traits<Sym>::find( attr_begin, attr_end - attr_begin, SaverTraits<Sym>::APOS ) )
			return save_attribute_internal_apos( name, attr_begin, attr_end );
		std::basic_string<Sym> new_attr;
		for( const Sym * c = attr_begin; c != attr_end; ++c )
			if( *c != SaverTraits<Sym>::QUOTE )
				new_attr.append( 1, *c );
			else
				new_attr.append( SaverTraits<Sym>::quote_alias.begin(), SaverTraits<Sym>::quote_alias.end() );
		return save_attribute_internal( name, new_attr.c_str(), new_attr.c_str() + new_attr.size() );
	}

protected:
	virtual bool save_attribute_internal( const std::string & name, const char * attr_begin, const char * attr_end )=0;
	virtual bool save_attribute_internal( const std::wstring & name, const wchar_t * attr_begin, const wchar_t * attr_end )=0;
	virtual bool save_attribute_internal_apos( const std::string & name, const char * attr_begin, const char * attr_end )=0;
	virtual bool save_attribute_internal_apos( const std::wstring & name, const wchar_t * attr_begin, const wchar_t * attr_end )=0;

public:

	virtual bool save_node( const char * name, const Object * obj )
	{
		return save_node( std::string( name ), obj );
	}

	virtual bool save_node( const wchar_t * name, const Object * obj )
	{
		return save_node( std::wstring( name ), obj );
	}

	virtual bool save_node( const std::string & name, const Object * obj )=0;
	virtual bool save_node( const std::wstring & name, const Object * obj )=0;


	template<class Dst>
	bool save_attribute( const char * name, const Dst & attr )
	{
		return save_attribute( std::string( name ), attr );
	}

	template<class Dst>
	bool save_attribute( const wchar_t * name, const Dst & attr )
	{
		return save_attribute( std::wstring( name ), attr );
	}

	template<class Dst>
	bool save_attribute( const std::string & name, const Dst & attr )
	{
		std::string tmp;
		converter::convert(attr, &tmp);
		return save_attribute_internal_correct_quote( name, tmp.c_str(), tmp.c_str() + tmp.size() );
	}

	template<class Dst>
	bool save_attribute( const std::wstring & name, const Dst & attr )
	{
		std::wstring tmp;
		converter::convert(attr, &tmp);
		return save_attribute_internal_correct_quote( name, tmp.c_str(), tmp.c_str() + tmp.size() );
	}

	virtual bool save_data( const void * data, unsigned size )=0;
	virtual ~SaverBase()
	{}
};

template<class Sym>
class SaverTextBase : public SaverBase
{
	enum last { NOTHING, ATTRIBUTE, NODE, DATA };
	last state;
	int level;
	textstream::TextOStream<Sym> * ts;

	void skip()
	{
		for( int i = 0; i != level; ++i )
			ts->print( &SaverTraits<Sym>::TAB, &SaverTraits<Sym>::TAB + 1 );
	}
protected:

	virtual bool save_attribute_internal( const std::basic_string<Sym> & name, const Sym * attr_begin, const Sym * attr_end )
	{
		ts->print( &SaverTraits<Sym>::SPACE, &SaverTraits<Sym>::SPACE + 1 );
		ts->print( name.c_str(), name.c_str() + name.size() );
		ts->print( SaverTraits<Sym>::attr_value_begin.begin(), SaverTraits<Sym>::attr_value_begin.end() );
		ts->print( attr_begin, attr_end );
		ts->print( &SaverTraits<Sym>::QUOTE, &SaverTraits<Sym>::QUOTE + 1 );
		state = ATTRIBUTE;
		return true;
	}

	virtual bool save_attribute_internal_apos( const std::basic_string<Sym> & name, const Sym * attr_begin, const Sym * attr_end )
	{
		ts->print( &SaverTraits<Sym>::SPACE, &SaverTraits<Sym>::SPACE + 1 );
		ts->print( name.c_str(), name.c_str() + name.size() );
		ts->print( SaverTraits<Sym>::attr_value_apos_begin.begin(), SaverTraits<Sym>::attr_value_apos_begin.end() );
		ts->print( attr_begin, attr_end );
		ts->print( &SaverTraits<Sym>::APOS, &SaverTraits<Sym>::APOS + 1 );
		state = ATTRIBUTE;
		return true;
	}

public:
	explicit SaverTextBase( textstream::TextOStream<Sym> * ts, bool write_header)
	:	ts( ts ),
		state( NODE ),
		level( 0 )
	{
		if( write_header )
			ts->print( SaverTraits<Sym>::header.begin(), SaverTraits<Sym>::header.end() );
	}

	virtual bool save_node( const Sym * name, const Object * obj )
	{
		return save_node( std::basic_string<Sym>( name ), obj );
	}

	virtual bool save_node( const std::basic_string<Sym> & name, const Object * obj )
	{
		switch( state )
		{
		case ATTRIBUTE:
		case NOTHING:
			ts->print( SaverTraits<Sym>::finish.begin(), SaverTraits<Sym>::finish.end() );
			break;
		};
		skip();
		ts->print( SaverTraits<Sym>::node_start.begin(), SaverTraits<Sym>::node_start.end() );
		ts->print( name.c_str(), name.c_str() + name.size() );
		state = NOTHING;
		++level;
		obj->on_save_attributes( this );
		obj->on_save_nodes( this );
		obj->on_save_data( this );
		--level;
		switch( state )
		{
		case NOTHING:
		case ATTRIBUTE:
			ts->print( SaverTraits<Sym>::empty_node_finish.begin(), SaverTraits<Sym>::empty_node_finish.end() );
			break;
		case DATA:
			ts->print( SaverTraits<Sym>::node_finish.begin(), SaverTraits<Sym>::node_finish.end() );
			ts->print( name.c_str(), name.c_str() + name.size() );
			ts->print( SaverTraits<Sym>::finish.begin(), SaverTraits<Sym>::finish.end() );
			break;
		default:
			skip();
			ts->print( SaverTraits<Sym>::node_finish.begin(), SaverTraits<Sym>::node_finish.end() );
			ts->print( name.c_str(), name.c_str() + name.size() );
			ts->print( SaverTraits<Sym>::finish.begin(), SaverTraits<Sym>::finish.end() );
		}
		state = NODE;
		return true;
	}

	virtual bool save_data( const void * data, unsigned size )
	{
		switch( state )
		{
		case ATTRIBUTE:
		case NOTHING:
			ts->print( &SaverTraits<Sym>::ANGULAR_BRACKET_RIGHT, &SaverTraits<Sym>::ANGULAR_BRACKET_RIGHT + 1 );
			break;
		};
		const Sym * begin = reinterpret_cast<const Sym *>( data );
		
		
		ts->print( &SaverTraits<Sym>::SEMICOLON, &SaverTraits<Sym>::SEMICOLON + 1 );

		ts->print( begin, begin + size/sizeof(Sym) );
		state = DATA;
		return true;
	}
	virtual ~SaverTextBase()
	{}
};

template<class Sym>
class SaverText
{};

template<>
class SaverText<char> : public SaverTextBase<char>
{
protected:

	virtual bool save_attribute_internal( const std::wstring & name, const wchar_t * attr_begin, const wchar_t * attr_end )
	{
		std::string avalue(attr_begin, attr_end);
		return SaverTextBase<char>::save_attribute_internal( std::string( name.begin(), name.end() ), avalue.c_str(), avalue.c_str() + avalue.size() );
	}

	virtual bool save_attribute_internal_apos( const std::wstring & name, const wchar_t * attr_begin, const wchar_t * attr_end )
	{
		std::string avalue(attr_begin, attr_end);
		return SaverTextBase<char>::save_attribute_internal_apos( std::string( name.begin(), name.end() ), avalue.c_str(), avalue.c_str() + avalue.size() );
	}

public:
	explicit SaverText( textstream::TextOStream<char> * ts, bool write_header = true )
		:	SaverTextBase<char>( ts, write_header )
	{}

	using SaverTextBase<char>::save_node;

	virtual bool save_node( const std::wstring & name, const Object * obj )
	{
		return SaverTextBase<char>::save_node( std::string( name.begin(), name.end() ), obj );
	}
};

template<>
class SaverText<wchar_t> : public SaverTextBase<wchar_t>
{
protected:
	virtual bool save_attribute_internal( const std::string & name, const char * attr_begin, const char * attr_end )
	{
		std::wstring avalue(attr_begin, attr_end);
		return SaverTextBase<wchar_t>::save_attribute_internal( std::wstring( name.begin(), name.end() ), avalue.c_str(), avalue.c_str() + avalue.size() );
	}

	virtual bool save_attribute_internal_apos( const std::string & name, const char * attr_begin, const char * attr_end )
	{
		std::wstring avalue(attr_begin, attr_end);
		return SaverTextBase<wchar_t>::save_attribute_internal_apos( std::wstring( name.begin(), name.end() ), avalue.c_str(), avalue.c_str() + avalue.size() );
	}

public:
	explicit SaverText( textstream::TextOStream<wchar_t> * ts, bool write_header = true )
		:	SaverTextBase<wchar_t>( ts, write_header )
	{}

	using SaverTextBase<wchar_t>::save_node;

	virtual bool save_node( const std::string & name, const Object * obj )
	{
		return SaverTextBase<wchar_t>::save_node( std::wstring( name.begin(), name.end() ), obj );
	}
};

/*class SaverBuffer : public textstream::Buffer<char>, public SaverText
{
public:
	explicit SaverBuffer(bool write_header = true)
	:	SaverText( this, write_header )
	{}
};*/


} // namespace xml
} // namespace liba

using namespace liba;

#endif //__XMLSAVER_H__